/*
Copyright (C) 2011 Salil Bhagurkar

This file is part of illusion

illusion is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

illusion is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with illusion.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <klib/format.h>
#include <klib/string.h>
#include <klib/lib.h>


static uint_t format_integer(char *buffer, uint_t index,
		uint_t buffer_size,
		uint_t number)
{
	uint_t div = 1000000000, q;
	char trail = 0;
	do {
		q = number / div;
		number = number - q * div;
		if(q || trail) {
			q = q + '0';
			if(index < buffer_size)
				buffer[index++] = q;
			trail = 1;
		}
		div = div / 10;
	} while(div > 0);
	if(!trail) {
		if(index < buffer_size)
			buffer[index++] = '0';
	}
	return index;
}

static uint_t format_hex(char *buffer, uint_t index,
		uint_t buffer_size, uint_t number)
{
	uint_t div = 0x10000000, q;
	char trail = 0;
	do {
		q = number / div;
		number = number - q * div;
		if(q > 0x9) {
			q = q - 0xa + 'a';
			if(index < buffer_size)
				buffer[index++] = q;
			trail = 1;
		} else {
			if(q || trail) {
				q = q + '0';
				if(index < buffer_size)
					buffer[index++] = q;
				trail = 1;
			}
		}
		div = div / 0x10;
	} while(div > 0);
	if(!trail) {
		if(index < buffer_size)
			buffer[index++] = '0';
	}
	return index;
}

uint_t format_string_va(char *buffer, uint_t buffer_size,
		char *fmt, va_list list)
{
	uint_t i, len = strlen(fmt);
	uint_t buffer_index = 0;
	for(i = 0; i < len; i++) {
		if(fmt[i] == '%') {
			if(fmt[i + 1] == 'u') {
				uint_t integer = va_arg(list, uint_t);
				buffer_index = format_integer(buffer, buffer_index, buffer_size, integer);
				i++;
			} else if(fmt[i + 1] == 'x') {
				uint_t uinteger = va_arg(list, uint_t);
				buffer_index = format_hex(buffer, buffer_index, buffer_size, uinteger);
				i++;
			} else if(fmt[i + 1] == 'p') {
				ptr_t uinteger = va_arg(list, ptr_t);
				buffer[buffer_index++] = '0';
				buffer[buffer_index++] = 'x';
				buffer_index = format_hex(buffer, buffer_index, buffer_size, uinteger);
				i++;
			} else if(fmt[i + 1] == 'c') {
				char c = va_arg(list, char);
				buffer[buffer_index++] = c;
				i++;
			} else if(fmt[i + 1] == 's') {
				char *str = va_arg(list, char *);
				if(str == null)
					str = "(null)";
				while((*str) != '\0')
					buffer[buffer_index++] = *str++;
				i++;
			} else if(fmt[i + 1] == '%') {
				buffer[buffer_index++] = '%';
				i++;
			} else {
				buffer[buffer_index++] = '%';
			}
		} else {
			buffer[buffer_index++] = fmt[i];
		}
	}
	buffer[buffer_index] = '\0';
	return buffer_index;
}

uint_t format_string(char *buffer, uint_t buffer_size, char *fmt, ...)
{
	va_list list;
	va_start(list, fmt);
	uint_t length = format_string_va(buffer, buffer_size, fmt, list);
	va_end(list);
	return length;
}

#define MAX_MULTIPLIER 1000000000
#define MAX_HEX_MULTIPLIER 0x10000000

uint_t dec2string(char *_str)
{
	uint_t len = strlen(_str);
	char *str = _str + len - 1;
	uint_t number = 0, multiplier = 1;
	while(str >= _str && multiplier < MAX_MULTIPLIER) {
		if((*str) >= '0' && (*str) <= '9') {
			number += ((*str) - '0') * multiplier;
			multiplier *= 10;
		}
		str--;
	}
	return number;
}

/*
 * Note that the string will include a '0' and 'x' at the beginning
 */
uint_t hex2string(char *_str)
{
	//Move past the '0x'
	_str += 2;
	uint_t len = strlen(_str);
	char *str = _str + len - 1;
	uint_t number = 0, multiplier = 1;
	while(str >= _str && multiplier <= MAX_HEX_MULTIPLIER) {
		if((*str) >= '0' && (*str) <= '9') {
			number += ((*str) - '0') * multiplier;
			multiplier *= 16;
		} else if((*str) >= 'a' && (*str) <= 'f') {
			number += ((*str) - 'a' + 10) * multiplier;
			multiplier *= 16;
		} else if((*str) >= 'A' && (*str) <= 'F') {
			number += ((*str) - 'A' + 10) * multiplier;
			multiplier *= 16;
		}
		str--;
	}
	return number;
}
